package
{
	import flash.display.*;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.sampler.NewObjectSample;
	
	public class PenTool extends CursorTool
	{
		// private objects
		private var drawPoints:Array = null;
		private var drawSegments:Array = null;
		private var oobshapes:Array = null;
		private var overDrawPoint:DrawPoint = null;
		private var selectedDrawPoint:DrawPoint = null;
		private var overDrawSegment:DrawSegment = null;
		private var selectedDrawSegment:DrawSegment = null;
		// private vars
		private var _state:String;
		private var _outOfBounds:Boolean = false;
		private var _freePointsRemaining:int;
		private var pwidth:Number;
		private var pheight:Number;
		private var pointsize:Number;
		private var enhancedsize:Number;
		
		public function get freePointsRemaining():int{return _freePointsRemaining;}
		override public function get outOfBounds():Boolean{return _outOfBounds;}
		override public function get components():Array
		{
			if (selectedDrawPoint != null)
			{
				return new Array(selectedDrawPoint);
			} else if (selectedDrawSegment != null)
			{
				return new Array(selectedDrawSegment);
			} else
			{
				return null;
			}
		}
		
		public function PenTool(drawPoints:Array, drawSegments:Array, oobshapes:Array, freePoints:int=-1)
		{
			super(this);
			this.drawPoints = drawPoints;
			this.drawSegments = drawSegments;
			this.oobshapes = oobshapes;
			this._freePointsRemaining = freePoints; 
			this.mouseEnabled = false;
			this.mouseChildren = false;
			this.addEventListener(Event.ADDED, handleAdded);
		}
		private function handleAdded(event:Event):void
		{
			this.removeEventListener(Event.ADDED, handleAdded);
			// for drag
			// for pen
			pwidth = 4*ShapeProductionTask.unit;
			pheight = 20*ShapeProductionTask.unit;
			pointsize = DrawPoint.POINT_SIZE*ShapeProductionTask.unit;
			enhancedsize = DrawPoint.ENHANCED_SIZE*ShapeProductionTask.unit;
			
			_state = "pen";
			drawCursor();
			// go through each point and add event listener
			for each (var p:DrawPoint in drawPoints)
			{
				p.mouseEnabled = true;
				if(!p.hasEventListener(MouseEvent.MOUSE_OVER)) p.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			}
			for each (var s:DrawSegment in drawSegments)
			{
				s.mouseEnabled = true;
				if(!s.hasEventListener(MouseEvent.MOUSE_OVER)) s.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
			}
			for each (var oobshape:OobShape in oobshapes)
			{
				oobshape.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);				
			}
			if (!this.parent.hasEventListener(MouseEvent.CLICK)) this.parent.addEventListener(MouseEvent.CLICK, handleClickEmptyPanel);
			
			this.addEventListener(Event.REMOVED, handleRemoved);			
		}
		
		/** Mouse over should work for all points to highlight except currently selected, unless allow disconnected */
		private function handleMouseOverPoint(event:MouseEvent):void
		{
			overDrawPoint = DrawPoint(event.currentTarget);
			overDrawPoint.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			overDrawPoint.enhance = true;
			overDrawPoint.addEventListener(MouseEvent.CLICK, handleClickPoint);	
			_state = "pen-yes";
			drawCursor();
			overDrawPoint.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
		}
		private function handleMouseOutPoint(event:MouseEvent):void
		{
			overDrawPoint.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
			overDrawPoint.removeEventListener(MouseEvent.CLICK, handleClickPoint);
			overDrawPoint.enhance = false;
			_state = "pen";
			drawCursor();
			overDrawPoint.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			overDrawPoint = null;
		}
		/** The click handler either adds a new segment if another point is selected or selects this point if no point is selected */
		private function handleClickPoint(event:MouseEvent):void
		{
			overDrawPoint.removeEventListener(MouseEvent.CLICK, handleClickPoint);
						
			// There is a point selected, draw a new segment to the overpoint, or if overpoint is the same selected, (if allowed) deselect it
			if (selectedDrawPoint != null)
			{ // If the point we are over is not selected, draw segment to it
				if (overDrawPoint != selectedDrawPoint)
				{
					var s:DrawSegment = DrawingPanel(parent).newSegment(selectedDrawPoint, overDrawPoint);
					s.mouseEnabled = true;
					s.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
					if (selectedDrawPoint != null) selectedDrawPoint.highlight = false;
					selectedDrawPoint = overDrawPoint;
					selectedDrawPoint.highlight = true;					
					//this.dispatchEvent(new Event(Event.COMPLETE));
					GeometryGameItem(parent.parent).processCommand("pen", "in_progress", this,new Array(selectedDrawPoint, freePointsRemaining));
				} else
				{  // If we are over the selected points, and we allow disconnected points, deselect it
					if (selectedDrawPoint != null) selectedDrawPoint.highlight = false;
					selectedDrawPoint = null;
				}
			} else
			{ // no point is selected, so select this one 
				selectedDrawPoint = overDrawPoint;
				selectedDrawPoint.highlight = true;					
			}		
		}
		/** Mouse over should work for all points to highlight except currently selected, unless allow disconnected */
		private function handleMouseOverSegment(event:MouseEvent):void
		{
			//trace("overDrawSegment", overDrawSegment);
			if (overDrawPoint == null)
			{
				overDrawSegment = DrawSegment(event.currentTarget);
				overDrawSegment.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
				overDrawSegment.enhance = true;
				overDrawSegment.addEventListener(MouseEvent.CLICK, handleClickSegment);	
				_state = "pen-yes";
				drawCursor();
				overDrawSegment.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutSegment);
			}
		}
		private function handleMouseOutSegment(event:MouseEvent):void
		{
			overDrawSegment.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutSegment);
			overDrawSegment.removeEventListener(MouseEvent.CLICK, handleClickSegment);
			overDrawSegment.enhance = false;
			_state = "pen";
			drawCursor();
			overDrawSegment.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
			overDrawSegment = null;
		}
		/** The click handler adds a new point and splits the segment into two new segments, removing the old segment */
		private function handleClickSegment(event:MouseEvent):void
		{
			var p:DrawPoint;
			if (Math.abs(freePointsRemaining) > 0)
			{
				overDrawSegment.removeEventListener(MouseEvent.CLICK, handleClickSegment);
				// make sure we can delete this segment
				if(DrawingPanel(parent).deleteSegment(overDrawSegment, true))
				{
					if (selectedDrawPoint != null) selectedDrawPoint.highlight = false;
					selectedDrawPoint = null;
					selectedDrawSegment = overDrawSegment;
					GeometryGameItem(parent.parent).processCommand("delete", "in_progress", this,new Array(overDrawSegment, freePointsRemaining));
					
					// now add point
					p = DrawingPanel(parent).newPoint();
					// we only want to add a listener if we are in the segment mode, will erase in the point mode using a different means
					p.mouseEnabled = true;
					p.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
					var _p:Point = MathUtilities.intersectionPerpPoints(new Point(parent.mouseX, parent.mouseY), overDrawSegment.point1.point, overDrawSegment.point2.point);
					p.moveTo(_p.x, _p.y);
					var s:DrawSegment = DrawingPanel(parent).newSegment(p, overDrawSegment.point1);
					s.mouseEnabled = true;
					s.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
					// new segment
					s = DrawingPanel(parent).newSegment(p, overDrawSegment.point2);
					s.mouseEnabled = true;
					s.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
					
					selectedDrawPoint = p;
					selectedDrawPoint.highlight = true;
					if (freePointsRemaining > 0) _freePointsRemaining--;
					GeometryGameItem(parent.parent).processCommand("pen", "in_progress", this,new Array(p, freePointsRemaining));
				}
				
			}
		}
		/** If a point is selected draw a new point to this location, if a point is not selected just draw a new point */
		private function handleClickEmptyPanel(event:MouseEvent):void
		{
			var p:DrawPoint;
			// overDrawPoint should be null be default here, but just checking
			// DRAW NEW POINT AND SEGMENT
			if (!_outOfBounds && overDrawPoint == null && overDrawSegment == null && Math.abs(freePointsRemaining) > 0)
			{
				if (selectedDrawPoint != null )
				{
					p = DrawingPanel(parent).newPoint("no", false, false, false);
					// we only want to add a listener if we are in the segment mode, will erase in the point mode using a different means
					p.mouseEnabled = true;
					p.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
					p.moveTo(parent.mouseX, parent.mouseY);
					var s:DrawSegment = DrawingPanel(parent).newSegment(p, selectedDrawPoint);
					s.mouseEnabled = true;
					s.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
					selectedDrawPoint.highlight = false;
					selectedDrawPoint = p;
					selectedDrawPoint.highlight = true;
				} else if (selectedDrawPoint == null)
				{ // overDrawPoint should still be null DRAW NEW POINT ONLY
					p = DrawingPanel(parent).newPoint();
					// we only want to add a listener if we are in the segment mode, will erase in the point mode using a different means
					p.mouseEnabled = true;
					p.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
					p.moveTo(parent.mouseX, parent.mouseY);
					selectedDrawPoint = p;
					selectedDrawPoint.highlight = true;
				}
				//this.dispatchEvent(new Event(Event.COMPLETE));
				if (freePointsRemaining > 0) _freePointsRemaining--;
				GeometryGameItem(parent.parent).processCommand("pen", "in_progress", this, new Array(p, freePointsRemaining));
			}
		}
		
		// out of bounds objects
		private function handleMouseOverOobShape(event:MouseEvent):void
		{
			event.currentTarget.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);
			_outOfBounds = true;
			_state = "pen-no";
			drawCursor();
			event.currentTarget.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);
		}
		private function handleMouseOutOobShape(event:MouseEvent):void
		{
			event.currentTarget.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);
			_outOfBounds = false;
			_state = "pen";
			drawCursor();
			event.currentTarget.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);
		}
		
		/** Process moves cursor, looks for out of bounds */
		override public function process(point:Point):void
		{			
			this.x = point.x;
			this.y = point.y;			
		}
		/** Draws a hand cursor to indicate the polygon being selected */
		private function drawCursor():void
		{
				
			switch (_state)
			{	
				case "pen":
					this.graphics.clear();
					// draw small circle
					this.graphics.beginFill(0xAAAAAA);
					this.graphics.drawEllipse(-pointsize/2, -pointsize/2, pointsize, pointsize);
					this.graphics.endFill();
					// draw pen
					this.graphics.lineStyle(pwidth/8,0x444444);
					this.graphics.beginFill(0x888888);
					this.graphics.moveTo(0,0);
					this.graphics.lineTo(pwidth/2,-pwidth);
					this.graphics.lineTo(-pwidth/2,-pwidth)
					this.graphics.lineTo(0,0);
					this.graphics.beginFill(0x8888FF);
					this.graphics.moveTo(pwidth/2,-pwidth);
					this.graphics.lineTo(pwidth/2,-pheight);
					this.graphics.lineTo(-pwidth/2,-pheight);
					this.graphics.lineTo(-pwidth/2,-pwidth)
					this.graphics.endFill();
					// handle
					this.graphics.beginFill(0x888888);
					this.graphics.drawEllipse(-pwidth/2,-pheight-pwidth/4,pwidth,pwidth/2);
					this.graphics.endFill();
					this.rotation=30;
					break;
				case "pen-yes":
					this.graphics.clear();
					this.graphics.lineStyle(1,0x000000);
					this.graphics.drawEllipse(-pointsize/2, -pointsize/4, pointsize, pointsize);
					// draw pen
					this.graphics.lineStyle(pwidth/8,0x444444);
					this.graphics.beginFill(0x888888);
					this.graphics.moveTo(0,0);
					this.graphics.lineTo(pwidth/2,-pwidth);
					this.graphics.lineTo(-pwidth/2,-pwidth)
					this.graphics.lineTo(0,0);
					this.graphics.beginFill(0x8888FF);
					this.graphics.moveTo(pwidth/2,-pwidth);
					this.graphics.lineTo(pwidth/2,-pheight);
					this.graphics.lineTo(-pwidth/2,-pheight);
					this.graphics.lineTo(-pwidth/2,-pwidth)
					this.graphics.endFill();
					// handle
					this.graphics.beginFill(0x888888);
					this.graphics.drawEllipse(-pwidth/2,-pheight-pwidth/4,pwidth,pwidth/2);
					this.graphics.endFill();
					this.rotation=30;
					break;
				case "pen-no":
					this.graphics.clear();
					this.graphics.lineStyle(1,0x000000);
					this.graphics.drawEllipse(-pointsize/2, -pointsize/2, pointsize, pointsize);
					this.graphics.lineStyle(2,0xFF0000);
					this.graphics.moveTo(-pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
					this.graphics.lineTo(pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
					this.graphics.moveTo(pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
					this.graphics.lineTo(-pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
					// draw pen
					this.graphics.lineStyle(pwidth/8,0x444444);
					this.graphics.beginFill(0x888888);
					this.graphics.moveTo(0,0);
					this.graphics.lineTo(pwidth/2,-pwidth);
					this.graphics.lineTo(-pwidth/2,-pwidth)
					this.graphics.lineTo(0,0);
					this.graphics.beginFill(0x8888FF);
					this.graphics.moveTo(pwidth/2,-pwidth);
					this.graphics.lineTo(pwidth/2,-pheight);
					this.graphics.lineTo(-pwidth/2,-pheight);
					this.graphics.lineTo(-pwidth/2,-pwidth)
					this.graphics.endFill();
					// handle
					this.graphics.beginFill(0x888888);
					this.graphics.drawEllipse(-pwidth/2,-pheight-pwidth/4,pwidth,pwidth/2);
					this.graphics.endFill();
					this.rotation=30;
					break;
				
			}
		}
		
		/** On removal unproject and unoutline */
		private function handleRemoved(event:Event):void
		{
			this.removeEventListener(Event.REMOVED, handleRemoved);
			if (overDrawPoint != null)
			{
				overDrawPoint.enhance = false;
				overDrawPoint.highlight = false;
			}
			if (selectedDrawPoint != null)
			{
				selectedDrawPoint.enhance = false;
				selectedDrawPoint.highlight = false;
			}			
			if (overDrawSegment != null)
			{
				overDrawSegment.enhance = false;
			}
			if (selectedDrawSegment != null)
			{
				selectedDrawSegment.enhance = false;
			}	
			for each (var p:DrawPoint in drawPoints)
			{
				if(p.hasEventListener(MouseEvent.MOUSE_OVER)) p.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
				if(p.hasEventListener(MouseEvent.MOUSE_OUT)) p.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
				if(p.hasEventListener(MouseEvent.CLICK)) p.removeEventListener(MouseEvent.CLICK, handleClickPoint);
				p.mouseEnabled = false;
			}
			for each (var s:DrawSegment in drawSegments)
			{
				if(s.hasEventListener(MouseEvent.MOUSE_OVER)) s.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
				if(s.hasEventListener(MouseEvent.MOUSE_OUT)) s.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutSegment);
				if(s.hasEventListener(MouseEvent.CLICK)) s.removeEventListener(MouseEvent.CLICK, handleClickSegment);
				s.mouseEnabled = false;
			}
			for each (var oobshape:OobShape in oobshapes)
			{
				if(oobshape.hasEventListener(MouseEvent.MOUSE_OVER)) oobshape.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);				
				if(oobshape.hasEventListener(MouseEvent.MOUSE_OUT)) oobshape.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);				
			}
			if (this.parent.hasEventListener(MouseEvent.CLICK)) this.parent.removeEventListener(MouseEvent.CLICK, handleClickEmptyPanel);
			
		}
	}
		
}